
//--Yangxin--

`include "defines.v"


module exe_stage(
	input  wire           				  clk              ,
	input  wire            				  reset            ,
	//allowin
	input  wire            				  ms_allowin       ,
	output wire          				  es_allowin       ,
	//from ds
	input  wire           				  ds_to_es_valid   ,
	input  wire [`DS_TO_ES_BUS_WD -1:0]   ds_to_es_bus     ,
	//to ms
	output wire          				  es_to_ms_valid   ,
	output wire [`ES_TO_MS_BUS_WD -1:0]   es_to_ms_bus     ,
	//forward
	output wire [				  69:0]   es_to_ds_fwd     ,
	//csr forward
	input  wire [                128:0]   csr_ms_to_es_fwd ,
    input  wire [				 128:0]   csr_ws_to_es_fwd ,
	//data sram interface
	//output wire          				  data_sram_en     ,
	//output wire [ 		       7:0]   data_sram_wen	   ,
	output wire                           data_sram_wr 	   ,
	output wire [				  63:0]   data_sram_addr   ,
	//output wire [				  63:0]   data_sram_mask   ,
	output wire [				  63:0]   data_sram_wdata  ,
	//output wire 						  data_sram_wr_rd  ,
	//add handshake
	output wire 						  data_sram_req    ,
	output wire [				   1:0]   data_sram_size   ,
	input  wire  						  data_sram_addr_ok,
	input  wire 						  data_sram_data_ok,
	
	output wire 						  es_valid_for_clint,

	//to csr_regs
	output wire [				  63:0]   csr_regs_raddr   ,
	input  wire [				  63:0]   csr_regs_rdata   ,
	//except
	input  wire 						  except_flush     ,
	output wire [                 63:0]   es_pc_to_ws      ,
	output wire                           es_valid_to_ws  			 
	);


assign es_valid_for_clint = es_valid;


wire          data_sram_en;
reg           es_valid;
wire          es_ready_go;
reg  [`DS_TO_ES_BUS_WD -1:0] ds_to_es_bus_r;
wire [15:0]   es_alu_op; 
wire          es_load_op;
wire  		  es_src2_is_sa;
wire  		  es_src1_is_pc;
wire          es_src2_is_imm;
wire          es_src2_is_4;
wire          es_alu_res_cut;
wire 		  es_gr_we;
wire 		  es_mem_we;
wire [4:0]    es_dest;
wire [63:0]   es_imm;
wire [63:0]   es_rs1_value;
wire [63:0]   es_rs2_value; 
wire [63:0]   es_pc;

wire [31:0]   es_inst;   //difftest

//csr
wire          es_csr_rd   ;
wire          es_csr_wr   ;
wire          es_csr_imm  ;
wire [11:0]   es_csr_waddr;
//exception
wire 		  es_except_enter_ecall;
wire          es_except_quit_mret ;

//load and store
wire [10:0] es_mem_control;
wire        data_mem_en;
assign data_mem_en = ^es_mem_control;

wire [7:0]  sram_wen   ;
wire [63:0] write_sram_wdata ;
wire        inst_is_sb ;
wire        inst_is_sh ;
wire        inst_is_sw ;
wire        inst_is_sd ;

assign inst_is_sb = es_mem_control[5] == 1'b1;
assign inst_is_sh = es_mem_control[4] == 1'b1;
assign inst_is_sw = es_mem_control[3] == 1'b1;
assign inst_is_sd = es_mem_control[0] == 1'b1;

assign write_sram_wdata = inst_is_sb ? (es_alu_result[2:0] == 3'b000 ? {56'h0, es_rs2_value[7:0]       } :
										es_alu_result[2:0] == 3'b001 ? {48'h0, es_rs2_value[7:0], 8'h0 } :
										es_alu_result[2:0] == 3'b010 ? {40'h0, es_rs2_value[7:0], 16'h0} :
										es_alu_result[2:0] == 3'b011 ? {32'h0, es_rs2_value[7:0], 24'h0} :
										es_alu_result[2:0] == 3'b100 ? {24'h0, es_rs2_value[7:0], 32'h0} :
										es_alu_result[2:0] == 3'b101 ? {16'h0, es_rs2_value[7:0], 40'h0} :
										es_alu_result[2:0] == 3'b110 ? {8'h0 , es_rs2_value[7:0], 48'h0} :
										/*es_alu_result[2:0] == 3'b111 ?*/ {       es_rs2_value[7:0], 56'h0} 
							           ) :
						  inst_is_sh ? (es_alu_result[2:1] == 2'b00 ? {48'h0,es_rs2_value[15:0]        } :
						  			    es_alu_result[2:1] == 2'b01 ? {32'h0,es_rs2_value[15:0],  16'h0} :
						  			    es_alu_result[2:1] == 2'b10 ? {16'h0,es_rs2_value[15:0],  32'h0} :
						  			    /*es_alu_result[2:1] == 2'b11 ?*/ {      es_rs2_value[15:0],  48'h0}  
						  	           ) :
						  inst_is_sw ? (es_alu_result[2] == 1'b0 ?    {32'h0,   es_rs2_value[31:0]     } :
						  				/*es_alu_result[2] == 1'b1 ?*/    {         es_rs2_value[31:0],32'h0}
						  			   ) :
						  inst_is_sd ? (es_rs2_value
						  	           ) :
						  	           es_rs2_value; 


assign sram_wen = inst_is_sb ? (es_alu_result[2:0] == 3'b000 ? 8'b0000_0001:
								es_alu_result[2:0] == 3'b001 ? 8'b0000_0010:
								es_alu_result[2:0] == 3'b010 ? 8'b0000_0100:
								es_alu_result[2:0] == 3'b011 ? 8'b0000_1000:
								es_alu_result[2:0] == 3'b100 ? 8'b0001_0000:
								es_alu_result[2:0] == 3'b101 ? 8'b0010_0000:
								es_alu_result[2:0] == 3'b110 ? 8'b0100_0000:
								/*es_alu_result[2:0] == 3'b111 ?*/ 8'b1000_0000
							    ) :
			      inst_is_sh ? (es_alu_result[2:1] == 2'b00 ?  8'b0000_0011:
						  	    es_alu_result[2:1] == 2'b01 ?  8'b0000_1100:
						  	    es_alu_result[2:1] == 2'b10 ?  8'b0011_0000:
						  	    /*es_alu_result[2:1] == 2'b11 ?*/  8'b1100_0000
						  	   ) :
				  inst_is_sw ? (es_alu_result[2] == 1'b0 ? 8'b0000_1111 :
						  		/*es_alu_result[2] == 1'b1 ?*/ 8'b1111_0000
						  	   ) :
				  inst_is_sd ? (8'b1111_1111
						  	   ) :
						  	    8'b1111_1111; 

wire [ 4:0]   EXE_dest;
assign EXE_dest = es_dest & {5{es_valid}};

//forward
//wire [69:0] es_to_ds_fwd;
assign es_to_ds_fwd = {70{es_valid}} & {70{es_gr_we}} & {es_load_op,es_dest,es_result};  //1+5+64
assign {es_except_enter_ecall,
	    es_except_quit_mret ,
		es_csr_rd      ,
		es_csr_wr      ,
		es_csr_imm     ,
		es_csr_waddr   ,
		es_inst        ,
		es_mem_control ,
		es_alu_op      ,
		es_load_op     ,
		es_src2_is_sa  ,
		es_src1_is_pc  ,
		es_src2_is_imm ,
		es_src2_is_4   ,
		es_alu_res_cut ,
		es_gr_we       ,
		es_mem_we      ,
		es_dest        ,
		es_imm         ,
		es_rs1_value   ,
		es_rs2_value   ,
		es_pc
	   } = ds_to_es_bus_r;

wire [63:0] es_alu_src1;
wire [63:0] es_alu_src2;
wire [63:0] es_alu_result;
wire [63:0] es_result;


wire	    es_res_from_mem;

assign es_res_from_mem = es_load_op;

assign es_to_ms_bus = {es_skip_mtimecmp     , //259:259
					   es_skip_mtime        , //258:258
					   es_skip              , //257:257
					   es_except_enter_ecall, //256:256
					   es_except_quit_mret , //255:255
	                   es_csr_wr      ,       //254:254
					   es_csr_wdata   ,       //253:190
					   es_csr_waddr   ,       //189:178
					   es_inst        ,       //177:146
					   es_mem_control ,       //145:135 
					   es_res_from_mem,       //134:134
					   es_gr_we       ,       //133:133
					   es_dest        ,       //132:128
					   es_result  ,           //127:64
					   es_pc                  //63:0        
   				      };

//assign es_ready_go = 1'b1;
assign es_ready_go = (data_mem_en) ? (es_skip ? 1'b1 : data_sram_req & data_sram_addr_ok) : 1'b1;
assign es_allowin  = !es_valid || es_ready_go && ms_allowin;
assign es_to_ms_valid = es_valid && es_ready_go;
always @(posedge clk) begin
	if (reset | except_flush) begin
		// reset
		es_valid <= 1'b0;
	end
	else if (es_allowin) begin
		es_valid <= ds_to_es_valid;
	end

	if(ds_to_es_valid && es_allowin) begin
		ds_to_es_bus_r <= ds_to_es_bus;
	end
end

assign es_pc_to_ws    = es_pc   ;
assign es_valid_to_ws = es_valid;

//csr
// module csr_reg(
// 	input wire         clk  ,
// 	input wire         reset,
// 	//from wb
// 	input wire         we   ,
// 	input wire  [63:0] raddr,
// 	input wire  [63:0] waddr,
// 	input wire  [63:0] wdata,
// 	output wire [63:0] rdata,

// 	output wire        global_int_en_o
	
// 	);


assign csr_regs_raddr = es_csr_rd ? {52'h0, es_inst[31:20]} : 64'h0;
wire [63:0] es_csr_true_rdata;  //need alu

//csr_regs_rdata need to be select(forward)
//wire [63:0] es_csr_rdata;
wire [63:0] es_csr_wdata;

assign es_csr_true_rdata = ((csr_regs_raddr == csr_ms_to_es_fwd[63:0]) && (csr_ms_to_es_fwd[128])) ? csr_ms_to_es_fwd[127:64] :
						   ((csr_regs_raddr == csr_ws_to_es_fwd[63:0]) && (csr_ws_to_es_fwd[128])) ? csr_ws_to_es_fwd[127:64] :
													                                                 csr_regs_rdata           ;

 //csr
 assign es_csr_wdata = es_csr_wr ? es_alu_result : 64'h0;


assign es_alu_src1 = //es_src1_is_sa ? {es_imm} :
					 es_csr_imm    ? {59'h0,es_inst[19:15]} :
					 es_src1_is_pc ? es_pc :
					 				 es_rs1_value;

assign es_alu_src2 = es_csr_rd     ? es_csr_true_rdata  :
					 es_src2_is_sa ? {58'd0,es_imm[5:0]}: 
					 es_src2_is_imm ? {es_imm} :
				     es_src2_is_4 ? 64'd4 :
				     				es_rs2_value;

//todo: alu 处理64I截断的问题，可能需要增加选择控制信号

alu inst_alu
	(
		.alu_op     (es_alu_op),
		.alu_src1   (es_alu_src1),
		.alu_src2   (es_alu_src2),
		.alu_result (es_alu_result)
	);


assign es_result =  es_csr_rd      ? es_csr_true_rdata                              :
					es_alu_res_cut ? {{32{es_alu_result[31]}}, es_alu_result[31:0]} :
								                               es_alu_result        ;

// mask_8_64 inst(.in(sram_wen),
// 		  .out(data_sram_mask));
wire [ 	    7:0]   data_sram_wen ;
wire [     63:0]   data_sram_mask; 

mask_8_64 inst(.in(data_sram_wen),
		  .out(data_sram_mask));

wire es_skip;
assign es_skip = (data_sram_addr == 64'h00000000_02004000 && data_mem_en) 
			   || (data_sram_addr == 64'h00000000_0200bff8 && data_mem_en);

wire es_skip_mtime;
wire es_skip_mtimecmp;
assign es_skip_mtime    = (data_sram_addr == 64'h00000000_0200bff8 && data_mem_en);
assign es_skip_mtimecmp = (data_sram_addr == 64'h00000000_02004000 && data_mem_en);


assign data_sram_en = (es_skip) ? 1'b0 : (data_mem_en & es_valid);
//assign data_sram_en = data_mem_en;
assign data_sram_wen = es_mem_we&&es_valid ? sram_wen : 8'h0;
assign data_sram_addr = data_mem_en ? es_alu_result : 0;
//assign data_sram_addr = {es_alu_result[63:2],2'b0};
//assign data_sram_addr = es_alu_result;
//assign data_sram_addr = (es_inst == 32'h00063783) ? 64'h80000000_00000000 : es_alu_result;
//assign data_sram_mask = ;
assign data_sram_wdata = write_sram_wdata;
assign data_sram_wr    = es_mem_we;

assign data_sram_req  = data_sram_en;
assign data_sram_size = (inst_is_sb) ? `SIZE_B :
						(inst_is_sh) ? `SIZE_H :
						(inst_is_sw) ? `SIZE_W :
						(inst_is_sd) ? `SIZE_D :
									   `SIZE_D ;


endmodule


module mask_8_64(
	input  wire [ 7:0] in,
	output wire [63:0] out
	);

genvar i;
generate for(i=0; i<64; i=i+1) begin : gen_for_mask_8_64
	integer j = i/8;
	assign out[i] = (in[j]) ? 1'b1 : 1'b0;
end endgenerate


endmodule